package models

import (
	"errors"
	"fmt"
	"strconv"
	"time"
)

type S背包列表 struct {
	Id   int32     `xorm:"autoincr pk"`
	Uid  int32     `xorm:"'uid'"`
	V类型  uint8     `xorm:"'类型'"`   //0: 自身背包  1：红背包 2：黄背包 3：蓝背包 4：个人背包  5公会背包
	V主类  string    `xorm:"'主类'"`   //根据这个主类的不同，可以在不同的城市创建不同的背包
	V行会名 string    `xorm:"'行会名'"`  //公会的名称
	Name string    `xorm:"'name'"` //这是仓库名称
	Time time.Time `xorm:"'time'"` //到期时间
	V未知1 int32     `xorm:"'未知1'"`
	V未知2 int32     `xorm:"'未知2'"`
	//Value string       //二进制信息
	Buff [9][13]int32 `xorm:"Buff"`
}

func (z *S背包列表) TableName() string {
	return "背包列表"
}
func (this *S背包列表) MarshalJSON() ([]byte, error) {
	return []byte(strconv.Itoa(int(this.Id))), nil
}

func (this S背包列表) UnmarshalJSON(value []byte) error {
	n, err := strconv.ParseInt(string(value), 10, 32)
	if err != nil {
		panic(err)
	}
	this.Id = int32(n)
	return nil
}

type S状态_仓库 struct {
	个人仓库 bool
	主类   string
}

func Int2String(bys []byte) string {
	ret := ""
	for _, v := range bys {
		ret += fmt.Sprintf("%02X ", v)
	}
	return ret
}

func 系统_保管箱服务() {
	for {
		time.Sleep(time.Second)
	}
}

//功能 将一个物品放置到指定位置
//注意 如果是从地上拾取的物品，物品自动改为正式物品，并存放到用户表中
//     如果是从其它位置放到这里的，成功后，需要将另一个地方的物品删除
//     调用此函数之前，必须先查找空位，这个函数只管存，不管检测
//     使用这个函数,说明以前一定不是在背包里放着

func (rt *RunTime) 背包_存放物品(背包 *S背包列表, x, y uint8, 物品 *Struct物品列表) error {
	if x < 0 || y < 0 {
		panic("")
	}
	if 背包.Time.Sub(time.Now()) < 0 {
		return errors.New("背包已过期")
	}
	if 物品.Id < 0 { //如果是临时物品，就需要从临时表中移除
		//m_物品列表.Delete(物品.Id) //从临时表中移除
		物品.Id = m_正式物品ID
		m_正式物品ID++
	}
	rt.V物品列表[物品.Id] = 物品
	物品信息 := 物品_获取物品信息(物品.V信息ID)
	for i := uint8(0); i < 物品信息.V宽度; i++ { //设置其它格为 已占用
		for ii := uint8(0); ii < 物品信息.V高度; ii++ {
			if (i == 0) && (ii == 0) {
				背包.Buff[x][y] = 物品.Id
			} else {
				背包.Buff[x+i][y+ii] = -物品.Id
			}
		}
	}
	return nil
}

//功能 将一个物品从背包中取出
func (rt *RunTime) 背包_取出指定位置物品(背包 *S背包列表, x uint8, y uint8, 数量 uint16) (*Struct物品列表, error) {
	if 背包.Time.Sub(time.Now()) < 0 {
		return nil, errors.New("背包已过期")
	}
	if (x < 0) || (x >= 9) || (y < 0) || (y >= 13) {
		return nil, errors.New("错误的坐标")
	}
	物品ID := 背包.Buff[x][y]
	if 物品ID <= 0 {
		return nil, errors.New("此位置为空")
	}
	物品, ok := rt.V物品列表[物品ID]
	if ok == false {
		return nil, errors.New("在物品列表中不存在!")
	}
	物品信息 := 物品_获取物品信息(物品.V信息ID)
	if 物品信息.V数量 == true {
		if 数量 == 0 {
			数量 = 物品.V数量
		}
		if 物品.V数量 < 数量 { //数量不够,不能取
			return nil, errors.New("数量不足")
		} else if 物品.V数量 > 数量 {
			新物品 := Struct物品列表{}
			新物品 = *物品
			新物品.Id = GetTempId()
			新物品.V数量 = 数量
			新物品.Stata.V新建 = true //标记为新建
			新物品.Stata.V更新字段 = make(map[string]bool)
			物品.V数量 -= 数量
			物品.Stata.V修改 = true
			物品.Stata.V更新字段["V数量"] = true
			return &新物品, nil
		}
	}
	for i := uint8(0); i < 物品信息.V宽度; i++ {
		for ii := uint8(0); ii < 物品信息.V高度; ii++ {
			背包.Buff[x+i][y+ii] = 0
		}
	}
	delete(rt.V物品列表, 物品ID)
	return 物品, nil
}
func (rt *RunTime) 背包_输出附助背包信息(背包 *S背包列表) {
	tx := CreateBuff(50)
	switch 背包.V类型 {
	case 1, 2, 3: //红蓝黄
		tx.AddInt16(0x01BA)
		tx.AddInt32(int32(背包.V类型 - 1))
		tx.AddStringAndLen(背包.Time.Format("2006-01-02"))
		if 背包.Time.Sub(time.Now()).Seconds() <= 0 { //超过时间就不能用了
			tx.AddInt32(0)
		} else {
			tx.AddInt32(1) //可能存在： 已经到期，但里面还有东西
		}
	default: //角色本身的背包，这里不作输出
		panic("")
	}
	tx.AddStringAndLen("")
	rt.AddHeadSend10(0x0A, [][]byte{{}, tx.Buff})
}

//由于登录时，背包中东西太多，造成登录不了，所以在登录的时候背包显示为空，当用户登录上以后，再进行背包同步
func (rt *RunTime) 背包_同步背包信息() {
	for 背包ID := uint8(0); 背包ID < 4; 背包ID++ {
		背包 := rt.背包_获取背包(背包ID)
		for x := uint8(0); x < 9; x++ {
			for y := uint8(0); y < 13; y++ {
				if 背包.Buff[x][y] > 0 {
					rt.同步_背包指定点(背包, x, y, false)
				}
			}
		}
	}
}

//
func (rt *RunTime) 背包_发送仓库物品列表(背包 *S背包列表) {
	tx := CreateBuff(50)
	tx.AddInt16(0X00f2)
	tx.AddInt32(背包.Id)

	数量 := int32(0)
	for x := uint8(0); x < 9; x++ {
		for y := uint8(0); y < 13; y++ {
			if 背包.Buff[x][y] > 0 {
				数量++
			}
		}
	}
	tx.AddInt32(数量) //内容数量
	for x := uint8(0); x < 9; x++ {
		for y := uint8(0); y < 13; y++ {
			if 背包.Buff[x][y] > 0 {
				物品 := rt.V物品列表[背包.Buff[x][y]]
				tx.AddInt32(int32(x))
				tx.AddInt32(int32(y))
				rt.物品_物品信息(tx, 物品)
			}
		}
	}
	rt.AddHeadSend10(0x0A, [][]byte{{}, tx.Buff})
}
func (rt *RunTime) 背包_输出登录信息(tx *Buff, 背包 *S背包列表) {
	switch 背包.V类型 {
	case 1, 2, 3: //红黄蓝
		tx.AddInt32(int32(背包.V类型 - 1))
		tx.AddInt32(0x09)
		tx.AddInt32(0x0D)
		tx.AddStringAndLen(背包.Time.Format("2006-01-02"))
		if 背包.Time.Sub(time.Now()).Seconds() <= 0 { //超过时间就不能用了
			tx.AddInt32(0)
		} else {
			tx.AddInt32(1) //可能存在： 已经到期，但里面还有东西
		}
	}
	tx.AddInt32(0)
}

//功能 根据背包的ID返回当前角色的背包
//参数  index 背包的ID,取值范围0-3 0为本身背包  1-3为附加背包
//返回值
//        index的背包
//        这个背包是否到期
func (rt *RunTime) 背包_获取背包(Index uint8) *S背包列表 {
	var 背包 *S背包列表
	switch Index {
	case 0:
		背包 = &rt.V角色.V背包
	case 1:
		背包 = &rt.V角色.V红背包
	case 2:
		背包 = &rt.V角色.V蓝背包
	case 3:
		背包 = &rt.V角色.V黄背包
	default:
		panic("")
	}
	if 背包.V类型 != Index {
		panic(fmt.Sprintf("当前选择的背包类型是:%d,实际找到的背包类型是:%d", Index, 背包.V类型))
	}
	return 背包 /*
		if (背包.Time.Sub(time.Now()).Seconds() > 0) || (Index == 0) {
			return 背包, true
		} else {
			return 背包, false
		}*/
}

//在指定背包中查找是否有合适位置
func (rt *RunTime) 背包_查询空位(背包 *S背包列表, 物品 *Struct物品列表) (uint8, uint8, bool) {
	if 背包.Time.Sub(time.Now()) < 0 {
		return 0, 0, false
	}
	物品信息 := 物品_获取物品信息(物品.V信息ID)
	for x := uint8(0); x < (9 - 物品信息.V宽度 + 1); x++ {
		for y := uint8(0); y < (13 - 物品信息.V高度 + 1); y++ {
			if 背包.Buff[x][y] != 0 {
				continue
			}
			if err := rt.背包_查询放置物品(背包, x, y, 物品); err == nil {
				return x, y, true
			}
		}
	}
	return 0, 0, false
}

//在指定背包中查找是否有合适位置
func (rt *RunTime) 背包_查询相同物品位置(背包 *S背包列表, 物品信息ID int32) (uint8, uint8, *Struct物品列表, bool) {
	if 背包.Time.Sub(time.Now()) < 0 {
		return 0, 0, nil, false
	}
	物品信息 := 物品_获取物品信息(物品信息ID)
	for x := uint8(0); x < (9 - 物品信息.V宽度 + 1); x++ {
		for y := uint8(0); y < (13 - 物品信息.V高度 + 1); y++ {
			物品ID := 背包.Buff[x][y]
			if 物品ID < 0 {
				continue
			}
			if 物品, ok := rt.V物品列表[物品ID]; ok == true {
				if 物品.V信息ID == 物品信息ID {
					return x, y, 物品, true
				}
			}
		}
	}
	return 0, 0, nil, false
}

//功能 查询物品是否可以放到指定位置
func (rt *RunTime) 背包_查询放置物品(背包 *S背包列表, x, y uint8, 物品 *Struct物品列表) error {
	if x < 0 {
		panic("X不能为-1")
	} else if y < 0 {
		panic("Y不能为-1")
	}
	if 背包.Time.Sub(time.Now()) < 0 {
		return errors.New("背包已过期")
	}
	物品信息 := 物品_获取物品信息(物品.V信息ID)
	for x1 := uint8(0); x1 < 物品信息.V宽度; x1++ {
		if (x + x1) >= 9 {
			return errors.New("X放不下")
		}
		for y1 := uint8(0); y1 < 物品信息.V高度; y1++ {
			if (y + y1) >= 13 {
				return errors.New("Y放不下")
			}
			if 背包.Buff[x+x1][y+y1] != 0 {
				return errors.New("物品重叠")
			}
		}
	}
	return nil
}

//功能 查询在指定位置的物品
func (rt *RunTime) 背包_查询位置物品(背包 *S背包列表, x uint8, y uint8) *Struct物品列表 {
	if 背包.Time.Sub(time.Now()) < 0 {
		return nil
	}
	if x >= 9 {
		return nil
	}
	if y >= 13 {
		return nil
	}
	物品ID := 背包.Buff[x][y]
	if 物品ID < 0 {
		物品ID *= 0
	}
	物品 := rt.V物品列表[物品ID]
	return 物品
}

//功能 双击
func (rt *RunTime) 背包_双击背包中的一格(背包ID uint8, x uint8, y uint8) {
	背包 := rt.背包_获取背包(背包ID)
	if 背包.Time.Sub(time.Now()) < 0 {
		return
	}
	if 背包.Buff[x][y] == 0 {
		return
	}
	物品ID := rt.V角色.V背包.Buff[x][y]
	if 物品ID < 0 {
		物品ID *= -1
	}
	物品 := rt.V物品列表[物品ID]
	物品信息 := 物品_获取物品信息(物品.V信息ID)
	if len(物品信息.V脚本) == 0 {
		return
	}
	if 物品信息.V数量 == true && 物品.V数量 > 0 {
		if len(物品信息.V脚本) != 0 {
			if rt.执行脚本(物品信息.V脚本, rt.V角色内容.Id) == false {
				return
			}
		}
		if 物品.V数量 == 1 {
			rt.背包_取出指定位置物品(背包, x, y, 1)
			db.Where("id=?", 物品.Id).Delete(物品)
			rt.同步_背包指定点(背包, x, y, false)
		} else {
			rt.背包_取出指定位置物品(背包, x, y, 1)
			rt.同步_背包指定点(背包, x, y, true)
		}
	} else {
		switch 物品信息.V外观2D {
		case "Eat_summonstone": //宠物
			rt.背包_取出指定位置物品(背包, x, y, 1)
			rt.装备_穿着指定位置物品(物品, E位置_宠物蛋)
			rt.同步_角色装备(E位置_宠物蛋, true)
		default:
			if rt.执行脚本(物品信息.V脚本, rt.V角色内容.Id) == false {
				return
			}
			rt.背包_取出指定位置物品(背包, x, y, 1)
			db.Where("id=?", 物品.Id).Delete(物品)
			rt.同步_背包指定点(背包, x, y, false)
		}
	}
}
func (rt *RunTime) 同步_背包指定点(背包 *S背包列表, x uint8, y uint8, 更新 bool) {
	//发送背包指定位置物品
	物品 := rt.背包_查询位置物品(背包, x, y)

	if (物品 == nil) || (更新 == true) {
		tx := CreateBuff(2 + 4 + 4 + 4)
		switch 背包.V类型 {
		case 0x00:
			tx.AddInt16(0x0030)
			tx.AddInt32(int32(x))
			tx.AddInt32(int32(y))
			tx.AddInt32(0) //2E
			rt.AddHeadSend10(0x0A, [][]byte{{}, tx.Buff})
		case 1, 2, 3: //红蓝黄背包
			tx.AddInt16(0x01B4)
			tx.AddInt32(int32(背包.V类型 - 1))
			tx.AddInt32(int32(x))
			tx.AddInt32(int32(y))
			rt.AddHeadSend10(0x0A, [][]byte{{}, tx.Buff})
		case 4: //个人背包
			tx.AddInt16(0x00FD)
			tx.AddInt32(背包.Id)
			tx.AddInt32(int32(x))
			tx.AddInt32(int32(y))
			rt.AddHeadSend10(0x0A, [][]byte{{}, tx.Buff})
		case 5: //公会背包

		}
		if 物品 == nil {
			return
		}
	}
	tx := CreateBuff(50)
	switch 背包.V类型 {
	case 0:
		tx.AddInt16(0x002F)
	case 1, 2, 3:
		tx.AddInt16(0x01B3)
		tx.AddInt32(int32(背包.V类型 - 1))
	case 4: //个人背包
		//tx.AddInt16(0X00f2)
		//tx.AddInt32(背包.Id)
		tx.AddInt16(0x00FB)
		tx.AddInt32(背包.Id)
	case 5: //公会背包
		panic("工会背包还没做")
	default:
		panic("")
	}
	tx.AddInt32(int32(x))
	tx.AddInt32(int32(y))
	rt.物品_物品信息(tx, 物品)
	rt.AddHeadSend10(0x0A, [][]byte{{}, tx.Buff})
}
func (rt *RunTime) 背包_同步到变量(背包 *S背包列表) {
	/*if len(背包.Value) == 0 {
		return
	}
	s := 背包.Value
	for x := uint8(0); x < 9; x++ {
		for y := uint8(0); y < 13; y++ {
			var 物品ID int32
			fmt.Sscanf(s[:8], "%08X", &物品ID)
			s = s[8:]
			if 物品ID <= 0 {
				continue
			}
			物品S := Fun加载物品([]int32{物品ID})
			if len(物品S) != 1 { //没有这个物品
				continue
			}
			if rt.背包_查询放置物品(背包, x, y, 物品S[0]) == nil {
				rt.背包_存放物品(背包, x, y, 物品S[0])
			} else {
				rt.背包_放置物品(物品S[0])
			}
		}
	}*/
}
func (rt *RunTime) 背包_同步到数据库(背包 *S背包列表) {
	/*str := ""
	for x := uint8(0); x < 9; x++ {
		for y := uint8(0); y < 13; y++ {
			值 := 背包.Buff[x][y]
			if 值 < 0 {
				值 = 0
			}
			str += fmt.Sprintf("%08X", 值)
		}
	}
	背包.Value = str*/
	db.Where("id=?", 背包.Id).Update(背包)
}

//功能 在自己的背包间移动
//返回 bool
//			true :	则表示物品被合并了，目标需要更新
//			False:	 则A移向了B，两个位置刷新
//     error
//			不为nil则表示出错
func (rt *RunTime) 背包_物品移动位置(原背包 *S背包列表, 原X, 原Y uint8, 目标背包 *S背包列表, 目标X, 目标Y uint8) (bool, error) {
	物品, err := rt.背包_取出指定位置物品(原背包, 原X, 原Y, 0)
	if err != nil {
		return false, err
	}
	成功 := false
	defer func() {
		if 成功 == false {
			rt.背包_存放物品(原背包, 原X, 原Y, 物品)
		}
	}()
	物品信息 := 物品_获取物品信息(物品.V信息ID)
	if 物品信息.V数量 == true {
		目标物品 := rt.背包_查询位置物品(目标背包, 目标X, 目标Y)
		if 目标物品 != nil {
			if 目标物品.V信息ID == 物品.V信息ID { //可以合并
				成功 = true
				db.Where("id=?", 物品.Id).Delete(物品)
				目标物品.V数量 += 物品.V数量
				目标物品.Stata.V修改 = true
				目标物品.Stata.V更新字段["V数量"] = true
				return true, nil
			}
		}
	}
	if err := rt.背包_查询放置物品(目标背包, 目标X, 目标Y, 物品); err != nil {
		return false, err
	}
	if err := rt.背包_存放物品(目标背包, 目标X, 目标Y, 物品); err != nil {
		return false, errors.New("神经病，取出后不能放回原位!")
	}
	成功 = true
	return false, nil
}

//整理背包
func (rt *RunTime) 背包_整理(背包 *S背包列表) bool {
	var 物品S []int32
	if 背包.Time.Sub(time.Now()) < 0 {
		return false
	}
	for y := uint8(0); y < 13; y++ {
		for x := uint8(0); x < 9; x++ {
			if 背包.Buff[x][y] > 0 {
				物品S = append(物品S, 背包.Buff[x][y])
			}
		}
	}
	/*新背包 := &S背包列表{}
	for _, 物品ID := range 物品S {
		物品 := rt.V物品列表[物品ID]
		if 物品 == nil {
			continue
		}

	}*/
	return false
}

//整理背包
func (rt *RunTime) 背包_放置物品(物品 *Struct物品列表) {
	物品信息 := 物品_获取物品信息(物品.V信息ID)
	for i := uint8(0); i < 4; i++ {
		背包 := rt.背包_获取背包(i)
		if 物品信息.V数量 == true {
			if x, y, 可合并物品, ok := rt.背包_查询相同物品位置(背包, 物品.V信息ID); ok == true {
				可合并物品.V数量 += 物品.V数量
				可合并物品.Stata.V修改 = true
				可合并物品.Stata.V更新字段["V数量"] = true
				rt.同步_背包指定点(背包, x, y, true)
				break
			}
		}
		if x, y, ok := rt.背包_查询空位(背包, 物品); ok == true {
			rt.背包_存放物品(背包, x, y, 物品)
			rt.同步_背包指定点(背包, x, y, false)
			break
		}
	}
}
